home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
Other Langs
/
Tickle-4.0 (tcl)
/
src
/
tar_list.c
< prev
next >
Wrap
Text File
|
1993-11-06
|
10KB
|
499 lines
#pragma segment TAR
/*
* Macintosh Tar
*
* Modified by Craig Ruff for use on the Macintosh.
*/
/*
* List a tar archive.
*
* Also includes support routines for reading a tar archive.
*
* Pubic Domain version written 26 Aug 1985 by John Gilmore (ihnp4!hoptoad!gnu).
*
* @(#)list.c 1.18 9/23/86 Public Domain - gnu
*/
#include "tar.h"
#include "stat.h"
#include <string.h>
char *ctime(); /* From libc.a */
#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
#define isspace(c) ((c) == ' ')
long FromOct(); /* Decode octal number */
union record *head; /* Points to current archive header */
struct
{
long st_size;
long st_mtime;
} hstat; /* Fake stat struct for compat. */
void PrintHeader();
void ReadAnd();
Boolean ListArchive(), SkipFile();
/*
* List - list an archive file
*/
#ifdef TCLAPPL
List()
{
Point where;
SFReply reply;
Boolean oldAutoPage = autoPage;
extern WindowPtr theFeedbackWindow;
/*
* Use standard file to get the archive file.
* Always do a screen at a time if to the screen.
*/
where.h = where.v = 75;
MyGetFile(where, "\pName of TAR file:", nil, -1, nil, nil, &reply);
if (!reply.good)
return;
arName = reply.fName;
WDDirVRef(reply.vRefNum, &arVRefNum, &arDirID);
if (WindInit())
return;
/*TGE*/ UBegYield();
/*TGE*/ ShowFeedback();
autoPage = true;
/*TGE*/ SetPort(theFeedbackWindow);
TextFace(underline);
WPrintf(header);
/*TGE*/ SetPort(theFeedbackWindow);
TextFace(0);
ReadAnd(ListArchive);
CloseArchive();
WPrintf("--- tar listing completed.");
WindEnd(true);
autoPage = oldAutoPage;
/*TGE*/ UEndYield();
}
#endif /* TCLAPPL */
Cmd_ListArchive(clientData, interp, argc, argv)
char *clientData;
Tcl_Interp *interp;
int argc;
char *argv[];
{
Boolean oldAutoPage = autoPage;
char name[256], *ptr;
struct stat statbuf;
#pragma unused (clientData)
if (argc != 3)
{
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" short|long archive_filename\"", (char *) NULL);
return TCL_ERROR;
}
if ( stat( argv[2], &statbuf ) < 0 )
{
Tcl_AppendResult(interp, "could not locate \"", argv[2],
"\" - ", Tcl_PosixError(interp), NULL);
return TCL_ERROR;
}
if ( S_ISDIR(statbuf.st_mode) )
{
Tcl_AppendResult(interp, "\"", argv[2], "\" is a directory", NULL);
return TCL_ERROR;
}
arVRefNum = statbuf.st_dev;
arDirID = statbuf.st_parid;
/*
* Extract and print the files as found in the archive.
*/
if (argv[1][0] == 'l' || argv[1][0] == 'L')
tar_list_format = 1;
else
tar_list_format = 0;
ptr = strrchr( argv[2], ':' );
if (ptr == NULL)
strcpy(name, argv[2]);
else
strcpy(name, ptr + 1);
c2pstr(name);
arName = name;
UBegYield();
autoPage = true;
if (tar_list_format)
{
Tcl_AppendResult( interp, header, (char *)0 );
Tcl_AppendResult( interp, "\015", (char *)0 );
}
tar_scripting = 1;
tar_listing = 1;
tar_interp = interp;
ReadAnd(ListArchive);
CloseArchive();
tar_scripting = 0;
tar_listing = 0;
tar_interp = NULL;
UEndYield();
autoPage = oldAutoPage;
return TCL_OK;
}
/*
* Main loop for reading an archive.
*/
void
ReadAnd(doSomething)
Boolean (*doSomething)();
{
int status = 1;
int prevStatus;
Boolean errFound = false;
CursHandle cursor;
extern int cancel_current_op;
extern short pause_op;
if ((cursor = GetCursor(watchCursor)) != nil)
SetCursor(*cursor);
OpenArchive(1); /* Open for reading */
while (!errFound) {
/*TGE*/ DoYield();
if (pause_op)
while (pause_op)
pausing();
if (cancel_current_op)
break;
/*TGE*/
#ifdef NEVER_DEFINED
EventRecord e;
SystemTask();
if (EventAvail(keyDownMask, &e) && (e.what != nullEvent)) {
if (
(e.modifiers & cmdKey) &&
((e.message & charCodeMask) == '.')
) {
GetNextEvent(keyDownMask, &e);
break;
}
}
#endif
prevStatus = status;
status = ReadHeader();
switch (status) {
case 1: /* Valid header */
/* We should decode next field (mode) first... */
/* Ensure incoming names are null terminated. */
head->header.name[NAMSIZ-1] = '\0';
errFound = (*doSomething)();
continue;
/*
* If the previous header was good, tell them
* that we are skipping bad ones.
*/
case 0: /* Invalid header */
case0:
UseRec(head);
if (prevStatus == 1) {
PgmAlert("\pReadAnd",
"\pSkipping to next file header...",
nil);
}
continue;
case 2: /* Block of zeroes */
if (ignorez)
goto case0; /* Just skip if asked */
/* FALL THRU */
case (int) EOF: /* End of archive */
break;
}
break;
}
CloseArchive();
SetCursor(&qd.arrow);
}
/*
* Print a header record, based on tar options.
*/
Boolean
ListArchive()
{
long t;
/* Save the record */
SaveRec(&head);
/*
* Print the header record.
* Don't sling the names too fast!
*/
PrintHeader();
if (!autoPage)
Delay(60L, &t);
/* Skip past it in the archive */
SaveRec((union record **) 0); /* Unsave it */
UseRec(head);
/* Skip to the next header on the archive */
return(SkipFile((long)hstat.st_size));
}
/*
* Read a record that's supposed to be a header record.
* Return its address in "head", and if it is good, the file's
* size in hstat.st_size.
*
* Return 1 for success, 0 if the checksum is bad, EOF on eof,
* 2 for a block full of zeros (EOF marker).
*
* You must always userec(head) to skip past the header which this
* routine reads.
*/
int
ReadHeader()
{
register int i;
register long sum, recsum;
register char *p;
register union record *header;
header = FindRec();
head = header; /* This is our current header */
if (header == nil)
return(EOF);
recsum = FromOct(8, header->header.chksum);
sum = 0;
p = header->charptr;
for (i = sizeof(*header); --i >= 0;) {
/*
* We can't use unsigned char here because of old compilers,
* e.g. V7.
*/
sum += 0xFF & *p++;
}
/* Adjust checksum to count the "chksum" field as blanks. */
for (i = sizeof(header->header.chksum); --i >= 0;)
sum -= 0xFF & header->header.chksum[i];
sum += ' ' * sizeof(header->header.chksum);
if (sum == recsum) {
/*
* Good record. Decode file size and return.
*/
hstat.st_size = FromOct(1+12, header->header.size);
return(1);
}
if (sum == 8 * ' ') {
/*
* This is a zeroed block...whole block is 0's except
* for the 8 blanks we faked for the checksum field.
*/
return(2);
}
return(0);
}
/*
* Decode things from a file header record into a "struct stat".
*
* read_header() has already decoded the checksum and length, so we don't.
*
* If wantug != 0, we want the uid/group info decoded from Unix Standard
* tapes (for extraction). If == 0, we are just printing anyway, so save time.
*/
DecodeHeader(header, st, wantug)
register union record *header;
register struct stat *st;
int wantug;
{
#pragma unused(wantug)
st->st_mtime = FromOct(1+12, header->header.mtime);
}
/*
* Quick and dirty octal conversion.
*
* Result is -1 if the field is invalid (all blank, or nonoctal).
*/
long
FromOct(digs, where)
register int digs;
register char *where;
{
register long value;
while (isspace(*where)) { /* Skip spaces */
where++;
if (--digs <= 0)
return(-1); /* All blank field */
}
value = 0;
while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */
value = (value << 3) | (*where++ - '0');
--digs;
}
if (digs > 0 && *where && !isspace(*where))
return(-1); /* Ended on non-space/nul */
return(value);
}
/*
* Actually print it.
*/
#define UGSWIDTH 9 /* min width of User, group, size */
#define DATEWIDTH 19 /* Last mod date */
static int ugswidth = UGSWIDTH; /* Max width encountered so far */
void
PrintHeader()
{
char mode;
char *timestamp;
char size[12]; /* Holds a formatted long */
long longie; /* To make ctime() call portable */
int pad;
DecodeHeader(head, &hstat, 0);
/* File type and mode */
mode = '?';
switch (head->header.linkflag) {
case LF_NORMAL:
case LF_OLDNORMAL:
mode = 'F';
if ('/' == head->header.name[strlen(head->header.name)-1])
mode = 'D';
break;
case LF_DIR:
mode = 'D';
break;
}
/*
* Convert to Mac based time from Unix based time.
*/
longie = hstat.st_mtime + TIMEDIFF;
timestamp = ctime(&longie);
timestamp[16] = '\0';
timestamp[24] = '\0';
/* Format the file size or major/minor device numbers */
switch (head->header.linkflag) {
default:
(void) sprintf(size, "?????");
break;
case LF_DIR:
(void) sprintf(size, "%.*s", UGSWIDTH, "");
break;
case LF_OLDNORMAL:
case LF_NORMAL:
(void) sprintf(size, "%ld", hstat.st_size);
break;
}
/* Figure out padding and print the whole line. */
pad = strlen(size) + 1;
if (pad > ugswidth)
ugswidth = pad;
if (tar_scripting && tar_listing)
{
char buffer[512];
if (tar_list_format)
{
sprintf(buffer, "%c %*s%s %s %s %.*s",
mode, ugswidth - pad, "", size,
timestamp+4, timestamp+20, sizeof(head->header.name),
head->header.name);
Tcl_AppendResult( tar_interp, buffer, "\015", (char *)0 );
}
else
{
sprintf(buffer, "%.*s", sizeof(head->header.name), head->header.name);
Tcl_AppendElement( tar_interp, buffer );
}
}
else
{
WPrintf("%c %*s%s %s %s %.*s",
mode, ugswidth - pad, "", size,
timestamp+4, timestamp+20, sizeof(head->header.name),
head->header.name);
}
}
/*
* Skip over <size> bytes of data in records in the archive.
*/
Boolean
SkipFile(size)
register long size;
{
union record *x;
while (size > 0) {
x = FindRec();
if (x == nil) { /* Check it... */
PgmAlert("\pSkipFile",
"\pUnexpected EOF on archive file",
nil);
return(true);
}
UseRec(x);
size -= RECORDSIZE;
}
return(false);
}